package com.dolphin.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.db.handler.EntityListHandler;
import cn.hutool.db.sql.SqlExecutor;
import cn.hutool.json.JSONArray;
import cn.hutool.setting.dialect.Props;
import com.alibaba.druid.pool.DruidDataSource;
import com.dolphin.commons.Constants;
import com.dolphin.commons.DynamicDataSource;
import com.dolphin.commons.GravatarUtil;
import com.dolphin.commons.OptionCacheUtil;
import com.dolphin.component.InitComponent;
import com.dolphin.model.*;
import com.dolphin.permission.AdminMenuGroup;
import com.dolphin.permission.MenuManager;
import com.dolphin.plugin.PluginManagerService;
import com.dolphin.service.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class InstallServiceImpl implements InstallService {
    private final static Logger LOGGER = LoggerFactory.getLogger(InstallServiceImpl.class);
    @Autowired
    private OptionService optionService;
    @Autowired
    private MenuService menuService;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private PluginService pluginService;

    @Value("${version}")
    private String version;

    @Autowired
    private PluginManagerService pluginManagerService;
    @Autowired
    private ArticleService articleService;
    @Autowired
    private WordService wordService;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private TagService tagService;
    @Autowired
    private CommentService commentService;
    @Autowired
    private LinkService linkService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ThemeService themeService;
    @Autowired
    private InitComponent initComponent;

    private static final int INSTALL_DATABASE_RESULT_SUCCESS = 200;
    private static final int INSTALL_DATABASE_RESULT_EXIST = -1;
    private static final int INSTALL_DATABASE_RESULT_SKIP = -2;
    /**
     * 初始化数据库类型:正常
     */
    private static final int INIT_INSTALL_DATABASE_TYPE_NORMAL = 1;
    /**
     * 初始化数据库类型:跳过
     */
    private static final int INIT_INSTALL_DATABASE_TYPE_SKIP = 2;

    public int addDatabase(Database database) throws Exception {
        DruidDataSource druidDataSource = DynamicDataSource.getDataSource();
        if (druidDataSource.isInited()) {
            druidDataSource.close();
            druidDataSource = new DruidDataSource();
        }
        File file = new File(Constants.DB_PROPERTIES_PATH);
        Props setting = new Props(FileUtil.touch(file), CharsetUtil.CHARSET_UTF_8);
        File sqlFile = new File("resources/dolphin.sql");
        if (!sqlFile.exists()) {
            sqlFile = FileUtil.file("dolphin.sql");
        }
        String url = "";
        // mysql
        if (database.getType().equals("mysql")) {
            String format = "jdbc:mysql://%s:%s/%s?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true";
            if (StringUtils.isBlank(database.getDataBaseName())) {
                database.setDataBaseName("dolphin");
            }
            url = String.format(format, database.getAddress(), database.getPort(), database.getDataBaseName());
            druidDataSource.setUrl(url);
            druidDataSource.setUsername(database.getUserName());
            druidDataSource.setPassword(database.getPassword());
            druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
            setting.setProperty("url", url);
            setting.setProperty("username", database.getUserName());
            setting.setProperty("password", database.getPassword());
            setting.setProperty("driverClassName", "com.mysql.jdbc.Driver");
            setting.setProperty("type", "mysql");
            setting.setProperty("ip", database.getAddress());
            setting.setProperty("port", database.getPort());
            setting.setProperty("dataBaseName", database.getDataBaseName());
        }
        SimpleDataSource ds = new SimpleDataSource(url, database.getUserName(), database.getPassword());
        Connection connection = null;
        try {
            connection = ds.getConnection();
            // 检测数据库是否存在
            if (database.getType().equals("mysql")) {
                List<Entity> entityList = SqlExecutor.query(connection, "select count(1) tableNumber from information_schema.TABLES WHERE table_schema = ?",
                        new EntityListHandler(), database.getDataBaseName());
                if (entityList != null && entityList.size() > 0 && entityList.get(0).getInt("tableNumber") > 0
                        && database.getInstallType() == INIT_INSTALL_DATABASE_TYPE_NORMAL) {
                    return INSTALL_DATABASE_RESULT_EXIST;
                }
            }

            if (database.getInstallType() == INIT_INSTALL_DATABASE_TYPE_SKIP) {
                setting.setProperty("installStatus", "dbSuccess");
                installInitOperate(setting, file);
                return INSTALL_DATABASE_RESULT_SKIP;
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("数据库连接失败:{}", e.getMessage());
            throw new Exception("数据库连接失败");
        } finally {
            if (connection != null) {
                connection.close();
            }
            ds.close();
        }
        DynamicDataSource.setDataSource(druidDataSource, setting.getStr("type"));
        initData(sqlFile);
        setting.setProperty("installStatus", "dbSuccess");
        setting.setProperty("dataVersion", version);
        installInitOperate(setting, file);
        return INSTALL_DATABASE_RESULT_SUCCESS;
    }

    /**
     * 初始化数据
     *
     * @param sqlFile sqlFile
     * @throws Exception e
     */
    private void initData(File sqlFile) throws Exception {
        Connection connection = DynamicDataSource.getDataSource().getConnection();
        FileReader fileReader = new FileReader(sqlFile);
        String createSql = fileReader.readString();
        String[] split = createSql.split(";");
        for (int i = 0; i < split.length - 1; i++) {
            connection.prepareStatement(split[i]).execute();
        }
        connection.close();

        Tag tag = initTag();
        Category category = initCategory();
        Article article = initArticle(tag, category);
        initOption();
        initRole();
        // initMenu();
        initPage();
        initJournal();
        initComment(article);
        initLink();
        initWord();
        initComponent.initScheduledTask();
    }

    private void initOption() {
        List<Option> optionList = new ArrayList<>();
        Option themeOption = new Option();
        themeOption.setOptionKey("WEB_THEME");
        themeOption.setOptionValue("default");
        optionList.add(themeOption);

        Option deskTopOption = new Option();
        deskTopOption.setOptionKey("admin_deskTop_backImage");
        deskTopOption.setRemark("桌面背景图");
        List list = new JSONArray();
        list.add("/static/public/libs/win10/img/wallpapers/main.jpg");
        list.add("/static/public/libs/win10/img/wallpapers/img1.jpg");
        list.add("/static/public/libs/win10/img/wallpapers/img2.jpg");
        list.add("/static/public/libs/win10/img/wallpapers/img3.jpg");
        list.add("/static/public/libs/win10/img/wallpapers/img4.jpg");
        deskTopOption.setOptionValue(list.toString());
        optionList.add(deskTopOption);

        Option isRegisterOption = new Option();
        isRegisterOption.setOptionKey("WEB_IS_REGISTER");
        isRegisterOption.setOptionValue("1");
        optionList.add(isRegisterOption);

        Option commentReviewOption = new Option();
        commentReviewOption.setOptionKey("WEB_COMMENT_IS_REVIEW");
        commentReviewOption.setOptionValue("0");
        optionList.add(commentReviewOption);

        Option wordApiOption = new Option();
        wordApiOption.setOptionKey(Constants.OPTION_WEB_WORD_API);
        wordApiOption.setOptionValue("https://v1.hitokoto.cn/?type=j&encode=text");
        optionList.add(wordApiOption);

        optionService.addOrUpdateOptions(optionList);
    }

    private void initRole() {
        Role adminRole = new Role();
        adminRole.setCode("admin");
        adminRole.setName("管理员");
        adminRole.setDescription("网站管理员");
        roleService.add(adminRole);

        Role userRole = new Role();
        userRole.setCode("user");
        userRole.setName("普通用户");
        userRole.setDescription("网站用户");
        roleService.add(userRole);

        Role editorRole = new Role();
        editorRole.setCode("editor");
        editorRole.setName("文章编辑");
        editorRole.setDescription("文章编辑");
        roleService.add(editorRole);

        Role contributeRole = new Role();
        contributeRole.setCode("contribute");
        contributeRole.setName("文章贡献");
        contributeRole.setDescription("文章贡献");
        roleService.add(contributeRole);
    }

    private void initMenu() {
        String currTheme = OptionCacheUtil.getValue(Constants.OPTION_WEB_THEME);
        Theme themeByPath = themeService.getThemeByPath(currTheme);
        if (themeByPath != null && StringUtils.isNotBlank(themeByPath.getType())) {
            List<Menu> themeMenu = themeService.getThemeMenu(themeByPath.getPath());
            if (themeMenu != null) {
                menuService.switchMenu(themeMenu, themeByPath);
            }
        }
    }

    private Tag initTag() {
        Tag tag = new Tag();
        tag.setColor("#36b368");
        tag.setName("演示标签");
        tag.setUserId(1L);
        tagService.add(tag);
        return tag;
    }

    private Category initCategory() {
        Category category = new Category();
        category.setName("演示分类");
        category.setStatus(0);
        category.setPid(-1L);
        categoryService.add(category);
        return category;
    }

    /**
     * 初始化文章数据
     */
    private Article initArticle(Tag tag, Category category) {
        Article article = new Article();
        article.setType(Constants.ARTICLE_TYPE_ARTICLE);
        article.setCategoryId(category.getId());
        ArticleTag articleTag = new ArticleTag();
        articleTag.setTagId(tag.getId());
        article.setArticleTags(ListUtil.toLinkedList(articleTag));
        article.setTitle("HelloWorld");
        article.setContent("欢迎使用 dolphin，如果您看到这篇文章,表示dolphin 已经安装成功.");
        article.setContentModel("markdown");
        article.setIsTop(0);
        article.setStatus(0);
        article.setIsComment(1);
        article.setUserId(1L);
        articleService.add(article);
        return article;
    }

    private void initPage() {
        // 友链
        Article linkArticle = new Article();
        linkArticle.setType(Constants.ARTICLE_TYPE_PAGE);
        linkArticle.setTitle("友链");
        linkArticle.setContent("友链页面,您可直接访问填写的访问地址进行查看,或者在菜单管理配置该访问地址~");
        linkArticle.setSlug("link");
        linkArticle.setContentModel("markdown");
        linkArticle.setIsTop(0);
        linkArticle.setStatus(0);
        linkArticle.setIsComment(1);
        linkArticle.setUserId(1L);
        articleService.add(linkArticle);

        Article messageArticle = new Article();
        messageArticle.setType(Constants.ARTICLE_TYPE_PAGE);
        messageArticle.setTitle("留言");
        messageArticle.setContent("留言页面,您可直接访问填写的访问地址进行查看,或者在菜单管理配置该访问地址~");
        messageArticle.setSlug("message");
        messageArticle.setContentModel("markdown");
        messageArticle.setIsTop(0);
        messageArticle.setStatus(0);
        messageArticle.setIsComment(1);
        messageArticle.setUserId(1L);
        articleService.add(messageArticle);

        Article aboutArticle = new Article();
        aboutArticle.setType(Constants.ARTICLE_TYPE_PAGE);
        aboutArticle.setTitle("关于");
        aboutArticle.setContent("关于页面,您可直接访问填写的访问地址进行查看,或者在菜单管理配置该访问地址~");
        aboutArticle.setSlug("about");
        aboutArticle.setContentModel("markdown");
        aboutArticle.setIsTop(0);
        aboutArticle.setStatus(0);
        aboutArticle.setIsComment(1);
        aboutArticle.setUserId(1L);
        articleService.add(aboutArticle);
    }

    private void initJournal() {
        // 友链
        Article message = new Article();
        message.setType(Constants.ARTICLE_TYPE_JOURNAL);
        message.setTitle(DateUtil.format(new Date(), "yyyy-MM-dd hh:mm:ss"));
        message.setContent("欢迎来到小海豚博客");
        message.setContentModel("markdown");
        message.setIsTop(0);
        message.setStatus(0);
        message.setIsComment(1);
        message.setUserId(1L);
        articleService.add(message);

    }

    private void initWord() {
        // 友链
        Word word = new Word();
        word.setText("这是美句，可以在后台美句功能增加也可以在设置-》其他设置里选择api模式");
        wordService.save(word);

    }

    private void initComment(Article article) {
        Comment comment = new Comment();
        comment.setArticleId(article.getId());
        comment.setEmail("dolphin@126.com");
        comment.setStatus(0);
        comment.setUserName("dolphin");
        comment.setContent("第一条评论");
        comment.setWebsite("https://www.wubaobaotools.cn");
        comment.setAvatar(GravatarUtil.getGravatar(comment.getEmail()));
        commentService.add(comment);
    }

    private void initLink() {
        Link link = new Link();
        link.setAddress("https://wubaobaotools.cn");
        link.setDesc("一款Java开发的博客/CMS系统");
        link.setLogo("https://wubaobaotools.cn/static/public/images/logo.png");
        link.setName("dolphin官网");
        linkService.add(link);
    }

    /**
     * @description 初始化
     * @author dolphin
     */
    private void installInitOperate(Props setting, File file) throws Exception {
        setting.store(file.getAbsolutePath());
        optionService.initOptionCache();
        List<Class<?>> classes = MenuManager.initSystemControllerClasses();
        //更新权限
        LOGGER.info(" >>>>>> 更新权限 开始!");
        List<Permission> permissionList = MenuManager.initSystemPermission(classes);
        permissionService.updatePermission(permissionList);
        permissionService.updatePermission();
        LOGGER.info(" >>>>>> 更新权限 结束!");
        //更新后端菜单
        LOGGER.info(" >>>>>> 更新菜单 开始!");
        List<AdminMenuGroup> adminMenuGroups = MenuManager.initSystemMenu();
        menuService.initSystemMenu(adminMenuGroups);
        //前端菜单
        initMenu();
        LOGGER.info(" >>>>>> 更新菜单 结束!");
        try {
            List<Plugin> plugins = pluginService.getAll();
            pluginManagerService.initPlugins(plugins);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void initSqliteFile(String filePath) throws Exception {
        File file = new File(filePath);

        File dir = file.getParentFile();
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                throw new Exception("数据库目录创建失败");
            }
        }

        if (!file.exists()) {
            try {
                if (!file.createNewFile()) {
                    throw new Exception("数据库文件创建失败");
                }
            } catch (IOException e) {
                throw new Exception("数据库文件创建失败" + e.getMessage());
            }
        }
    }

}
